#include "compress.h"

Filemsg filemsg;

int node_cmp(const void* p1,const void* p2)
{
	if(((TreeNode*)p1)->weight > ((TreeNode*)p2)->weight)
		return 1;
	if(((TreeNode*)p1)->weight < ((TreeNode*)p2)->weight)
		return -1;
	return 0;
}

TreeNode* create_node(unsigned char val,size_t weight)
{
	TreeNode* node = malloc(sizeof(TreeNode));
	node->val = val;
	node->weight = weight;
	node->left = NULL;
	node->right = NULL;
	return node;
}

CharCode code_arr[256];
void _create_code(TreeNode* root,char path,size_t code,int cnt)
{
	if(NULL == root)
		return;

	code = (code<<1) + path;

	if(0 != root->val)
	{
		code_arr[root->val].val = root->val;
		code_arr[root->val].code = code;
		code_arr[root->val].cnt = cnt;
		return;
	}

	_create_code(root->left,0,code,cnt+1);
	_create_code(root->right,1,code,cnt+1);
}

void create_code(TreeNode* root)
{
	_create_code(root->left,0,0,1);
	_create_code(root->right,1,0,1);
}

TreeNode* init_tree(unsigned char* val_arr,size_t* weight_arr,size_t len)
{
	// 创建存储森林的小根堆
	Heap* heap = create_heap(len,node_cmp);
	for(int i=0; i<len; i++)
	{
		push_heap(heap,create_node(val_arr[i],weight_arr[i]));
	}

	while(1 < size_heap(heap))
	{
		TreeNode* left = top_heap(heap);
		pop_heap(heap);

		TreeNode* right = top_heap(heap);
		pop_heap(heap);

		TreeNode* root = create_node(0,left->weight+right->weight);
		root->left = left;
		root->right = right;

		push_heap(heap,root);
	}

	TreeNode* root = top_heap(heap);
	destroy_heap(heap);
	
	return root;
}

void get_hafftree(FILE* rfp,const char* filename)			//用于读取文件权值并生成对应的hafftree
{
	unsigned char data[4096];
    size_t hash[256] = {0};
    int ret;
    while(0 != (ret = fread(data,1,sizeof(data),rfp)))
    {
        for(int i=0;i<ret;i++)
        {
            hash[data[i]]++;
        }
    }
	rewind(rfp);

	int sum = 0;
	unsigned char val_arr[256] = {0};
	size_t weight_arr[256] = {0};
	for(int i=0;i<256;i++)				//记录权值非零的字符到数组中
	{
		if(hash[i] > 0)
		{
			val_arr[sum] = i;
			weight_arr[sum] = hash[i];
			sum++;
		}
	}
	TreeNode* root = init_tree(val_arr,weight_arr,sizeof(val_arr));
	create_code(root);

	char json_filename[255];					//用于存储hafftree权值数据的文件名
	change_json(json_filename,filename);

	FILE* wfp = fopen(json_filename,"wb");			//将权值数组写入文件
	fwrite(&sum,sizeof(int),1,wfp);
	fwrite(val_arr,sizeof(unsigned char),sum,wfp);
	fwrite(weight_arr,sizeof(size_t),sum,wfp);
	fclose(wfp);
}

int compress_file(const char* filename,FILE* rfp)
{
	int ret;
	unsigned char buf[4096] = {0};
	unsigned int data = 0;
	size_t cnt = 0,offset=0xffffffff; 
	
	char w_filename[30] = {0}; 
	change_dat(w_filename,filename);
	FILE* wfp = fopen(w_filename,"w");
	if(NULL == wfp)
	{
		printf("压缩文件失败！\n");
		return -1;
	}
	fwrite_filemsg(filename,rfp,wfp);
	while(0 != (ret = fread(buf,1,sizeof(buf),rfp)))
	{
		for(int i=0; i<ret; i++)
		{
			if(32 > cnt + code_arr[buf[i]].cnt)
			{
				data = (data << code_arr[buf[i]].cnt) + code_arr[buf[i]].code;
				cnt += code_arr[buf[i]].cnt;
			}
			else
			{
				data = (data <<(32-cnt)) + (code_arr[buf[i]].code >> (code_arr[buf[i]].cnt - (32-cnt)));
				fwrite(&data,1,sizeof(data),wfp);
				data = (~(offset<<(code_arr[buf[i]].cnt-(32-cnt))))&code_arr[buf[i]].code;
				cnt = code_arr[buf[i]].cnt-(32-cnt);
			}
		}
	}
	data = data << (32-cnt);
	fwrite(&data,1,sizeof(data),wfp);
	fclose(wfp);
	return 0;
}

void fwrite_filemsg(const char* filename,FILE* rfp,FILE* wfp)  		//记录文件相关信息
{
	unsigned char buf[4096] = {0};
	int ret;
	filemsg.sum_word = 0;
	while(0 != (ret = fread(buf,1,sizeof(buf),rfp)))				//计算文件总字节数
	{
		for(int i=0;i<ret;i++)
		{
			if(buf[i]>=0)
				filemsg.sum_word++;
		}
		memset(buf,-1,sizeof(buf));
	}
	strcpy(filemsg.filename,filename);
	//printf("\n%lld %d %s\n",filemsg.sum_word,filemsg.len_filename,filemsg.filename);
	fwrite(&filemsg,sizeof(filemsg),1,wfp);						//写入文件开头
	rewind(rfp);
}

void change_dat(char* dest,const char* filename)			//将压缩文件文件名设置为 源文件名.dat
{
	char* tmp = dest;
	while('.' != *filename)
	{
		*tmp++ = *filename++;
	}
	tmp++;
	*tmp = '\0';
	strcat(dest,".dat");	
}

void change_json(char* dest,const char* filename)			//将压缩文件文件名设置为 源文件名.json
{
	char* tmp = dest;
	while('.' != *filename)
	{
		*tmp++ = *filename++;
	}
	tmp++;
	*tmp = '\0';
	strcat(dest,".json");	
}

void get_haffcode(const char* filename,unsigned char* val_arr,size_t* weight_arr)
{
	char json_filename[255] = {0};
	change_json(json_filename,filename);
	FILE* rfp = fopen(json_filename,"r");
	if(NULL == rfp)
	{
		printf("%s文件打开失败!\n",json_filename);
		return;
	}
	int sum;
	fread(&sum,sizeof(int),1,rfp);
	fread(val_arr,sizeof(unsigned char),sum,rfp);
	fread(weight_arr,sizeof(size_t),sum,rfp);
	fclose(rfp);
}

int dp_file(TreeNode* root,FILE* rfp)
{
	fread(&filemsg,sizeof(filemsg),1,rfp);
	FILE* wfp = fopen(filemsg.filename,"w");
	if(NULL == wfp)
	{
		printf("解压失败！\n");
		return -1;
	}

	unsigned int data;
	TreeNode* node = root;

	while(fread(&data,1,sizeof(data),rfp))
	{
		for(int i=0; i<32; i++)
		{
			if((data << i)&0x80000000)
				node = node->right;
			else
				node = node->left;
			if(0 != node->val)
			{
				fwrite(&node->val,1,1,wfp);
				filemsg.sum_word--;
				node = root;
			}
			if(0 == filemsg.sum_word)
			{
				fclose(wfp);
				return 0;
			}
		}
	}
	fclose(wfp);
	return 0;
}